import { _decorator, Component, Vec2, Vec3, find, Camera, UITransform } from 'cc';
import { Clock } from '../core/Clock';
import InputBridge, { getMoveVec } from '../input/InputBridge';
import { TICK_RATES } from '../shared';

const { ccclass, property } = _decorator;

@ccclass('PlayerKinematicController')
export class PlayerKinematicController extends Component {
  @property
  public speed = 280; // 像素/秒（2D UI 坐标）
  @property
  public useSmoothing = true;
  @property
  public damping = 10; // 趋近速率（越大越快趋近）
  @property
  public clampToView = true;
  @property
  public radius = 16; // 半径用于边界留白
  @property
  public recenterOnEnable = true;

  private clockKey = '';
  private clock: Clock | null = null;
  private input: InputBridge | null = null;
  private tmp = new Vec2();
  private pos3 = new Vec3();
  private vel = new Vec2();
  private cam: Camera | null = null;
  private canvasUi: UITransform | null = null;

  onEnable() {
    // Ensure input and clock exist on the same node
    this.input = this.node.getComponent(InputBridge) ?? this.node.addComponent(InputBridge);
    this.clock = this.node.getComponent(Clock) ?? this.node.addComponent(Clock);

    this.clockKey = `player-move-${this.node.uuid}`;
    this.clock.register(this.clockKey, TICK_RATES.render, (dt) => this.onTick(dt));

    // Cache camera and canvas for bounds
    this.cam = find('Canvas/Camera')?.getComponent(Camera) ?? null;
    this.canvasUi = find('Canvas')?.getComponent(UITransform) ?? null;

    if (this.recenterOnEnable && this.cam) {
      const c = this.cam.node.worldPosition;
      this.node.setWorldPosition(c.x, c.y, 0);
    }
  }

  onDisable() {
    this.clock?.unregister(this.clockKey);
  }

  onDestroy() {
    this.clock?.unregister(this.clockKey);
  }

  private onTick(dt: number) {
    // Prefer module-level accessor; fall back to instance snapshot
    const mv = getMoveVec?.() ?? this.input?.snapshot().move ?? this.tmp.set(0, 0);
    const desired = new Vec2();
    if (mv.x !== 0 || mv.y !== 0) {
      desired.set(mv.x, mv.y);
      if (desired.lengthSqr() > 1) desired.normalize();
      desired.multiplyScalar(this.speed);
    } else {
      desired.set(0, 0);
    }

    if (this.useSmoothing) {
      const alpha = 1 - Math.exp(-this.damping * dt);
      // v += (desired - v) * alpha
      this.vel.x += (desired.x - this.vel.x) * alpha;
      this.vel.y += (desired.y - this.vel.y) * alpha;
    } else {
      this.vel.set(desired.x, desired.y);
    }

    if (this.vel.lengthSqr() === 0) {
      return;
    }

    this.node.getWorldPosition(this.pos3);
    this.pos3.x += this.vel.x * dt;
    this.pos3.y += this.vel.y * dt;

    if (this.clampToView && this.cam && this.canvasUi) {
      const halfH = this.cam.orthoHeight ?? 0;
      const aspect = this.canvasUi.contentSize.height > 0 ? (this.canvasUi.contentSize.width / this.canvasUi.contentSize.height) : 16 / 9;
      const halfW = halfH * aspect;
      const pad = this.radius;
      // Use camera world center as clamp center (Canvas is usually offset e.g. 640,360)
      const camPos = this.cam.node.worldPosition;
      const minX = camPos.x - halfW + pad;
      const maxX = camPos.x + halfW - pad;
      const minY = camPos.y - halfH + pad;
      const maxY = camPos.y + halfH - pad;
      this.pos3.x = Math.min(maxX, Math.max(minX, this.pos3.x));
      this.pos3.y = Math.min(maxY, Math.max(minY, this.pos3.y));
    }

    this.node.setWorldPosition(this.pos3);
    if (typeof console !== 'undefined' && console.debug) {
      console.debug('[MOVE] pos(', this.pos3.x.toFixed(1), ',', this.pos3.y.toFixed(1), ') vec(', this.vel.x.toFixed(2), ',', this.vel.y.toFixed(2), ')');
    }
  }
}

export default PlayerKinematicController;
